// Copyright (c) 2008 Michael Buckley

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#import "BTLSocketUDPTests.h"
#import "BTLUDPSocketTestDelegate.h"

@implementation BTLSocketUDPTests

- (void)testManagedUDPTPv4Socket
{
	BTLSocketIPAddress* addr1 = [BTLSocketIPAddress addressWithHostname:@"localhost" port:0 family:AF_INET error:nil];
	BTLSocketIPAddress* addr2 = [BTLSocketIPAddress addressWithHostname:@"127.0.0.1" port:0 family:AF_INET error:nil];
	
	[self testManagedUDPSocketWithSourceAddress:addr1 withDestinationAddress:addr2];
	[self testUnmanagedUDPSocketWithSourceAddress:addr1 withDestinationAddress:addr2];
	[self testUnmanagedDelegatelessUDPSocketWithSourceAddress:addr1 withDestinationAddress:addr2];
}

- (void)testManagedUDPSocketWithSourceAddress:(BTLSocketAddress*)addr1 withDestinationAddress:(BTLSocketAddress*)addr2
{
	if(addr1 == nil || addr2 == nil){
		return;
	}
	
	BTLUDPSocketTestDelegate* testDelegate1 = [BTLUDPSocketTestDelegate new];
	BTLUDPSocketTestDelegate* testDelegate2 = [BTLUDPSocketTestDelegate new];
	BTLSocketManager* testManager = [BTLSocketManager new];
	
	BTLUDPSocket* sock1 = (BTLUDPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_DGRAM];
	STAssertTrue([sock1 bindToAddress:addr1], nil);
	[sock1 setDelegate:testDelegate1];
	[sock1 setManager:testManager];
	
	BTLUDPSocket* sock2 = (BTLUDPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_DGRAM];
	STAssertTrue([sock2 bindToAddress:addr2], nil);
	[sock2 setDelegate:testDelegate2];
	[sock2 setManager:testManager];
	
	[testDelegate1 setForbiddenAddress:[sock2 localAddress]];
	
	BTLSocketBuffer* data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock1 writeData:data1 toAddress:[sock2 localAddress]];
	
	STAssertFalse([sock1 isConnected], nil);
	
	[testDelegate1 setForbiddenAddress:nil];
	[sock1 writeData:data1 toAddress:[sock2 localAddress]];
	
	STAssertTrue([sock1 isConnected], nil);
	STAssertTrue([testDelegate1 currentlyConnected], nil);
	
	[testManager select];
	BTLSocketBuffer* testData = [testDelegate2 lastMessage];
	
	while(testData == nil){
		[testManager select];
		testData = [testDelegate2 lastMessage];
	}
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"First data not equal", nil);
	}
	
	STAssertTrue([sock2 isConnected], nil);
	STAssertTrue([testDelegate2 currentlyConnected], nil);
	
	data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock2 writeData:data1 toAddress:[sock1 localAddress]];
	
	[testManager select];
	testData = [testDelegate1 lastMessage];
	
	while(testData == nil){
		[testManager select];
		testData = [testDelegate1 lastMessage];
	}
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"Second data not equal");
	}
	
	srand(time(0));
	char buffer[3000];
	int i;
	for(i = 0; i < 3000; ++i){
		buffer[i] = rand() % 256; // Who cares about mod bias?
	}
	
	data1 = [BTLSocketBuffer new];
	[data1 addData:buffer ofSize:3000];
	[sock1 writeData:data1 toAddress:[sock2 localAddress]];
	
	[testManager select];
	testData = [testDelegate2 lastMessage];
	
	while(testData == nil){
		[testManager select];
		testData = [testDelegate2 lastMessage];
	}
	
	STAssertEquals([testData size], [data1 size], nil);
	
	const char* result = [testData rawData];
	for(i = 0; i < 3000; ++i){
		if(result[i] != buffer[i]){
			STFail(@"Char buffers not equal at iteration %d", i, nil);
			break;
		}
	}
	
	[sock1 closeConnectionToAddress:[sock2 localAddress]];
	STAssertFalse([sock1 isConnected], nil);
	STAssertFalse([testDelegate1 currentlyConnected], nil);
	[sock2 connectionInterruptedToAddress:[sock1 localAddress]];
	STAssertFalse([sock2 isConnected], nil);
	STAssertFalse([testDelegate2 currentlyConnected], nil);
}

- (void)testUnmanagedUDPSocketWithSourceAddress:(BTLSocketAddress*)addr1 withDestinationAddress:(BTLSocketAddress*)addr2
{
	if(addr1 == nil || addr2 == nil){
		return;
	}
	
	BTLUDPSocketTestDelegate* testDelegate1 = [BTLUDPSocketTestDelegate new];
	BTLUDPSocketTestDelegate* testDelegate2 = [BTLUDPSocketTestDelegate new];
	
	BTLUDPSocket* sock1 = (BTLUDPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_DGRAM];
	STAssertTrue([sock1 bindToAddress:addr1], nil);
	[sock1 setDelegate:testDelegate1];
	
	BTLUDPSocket* sock2 = (BTLUDPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_DGRAM];
	STAssertTrue([sock2 bindToAddress:addr2], nil);
	[sock2 setDelegate:testDelegate2];
	
	[testDelegate1 setForbiddenAddress:[sock2 localAddress]];
	
	BTLSocketBuffer* data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock1 writeData:data1 toAddress:[sock2 localAddress]];
	
	STAssertFalse([sock1 isConnected], nil);
	
	[testDelegate1 setForbiddenAddress:nil];
	[sock1 writeData:data1 toAddress:[sock2 localAddress]];
	
	STAssertTrue([sock1 isConnected], nil);
	STAssertTrue([testDelegate1 currentlyConnected], nil);
	
	[sock2 read];
	BTLSocketBuffer* testData = [testDelegate2 lastMessage];
	
	while(testData == nil){
		[sock2 read];
		testData = [testDelegate2 lastMessage];
	}
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"First data not equal", nil);
	}
	
	STAssertTrue([sock2 isConnected], nil);
	STAssertTrue([testDelegate2 currentlyConnected], nil);
	
	data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock2 writeData:data1 toAddress:[sock1 localAddress]];
	
	[sock1 read];
	testData = [testDelegate1 lastMessage];
	
	while(testData == nil){
		[sock1 read];
		testData = [testDelegate1 lastMessage];
	}
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"Second data not equal");
	}
	
	[sock1 closeConnectionToAddress:[sock2 localAddress]];
	STAssertFalse([sock1 isConnected], nil);
	STAssertFalse([testDelegate1 currentlyConnected], nil);
	[sock2 connectionInterruptedToAddress:[sock1 localAddress]];
	STAssertFalse([sock2 isConnected], nil);
	STAssertFalse([testDelegate2 currentlyConnected], nil);
}


- (void)testUnmanagedDelegatelessUDPSocketWithSourceAddress:(BTLSocketAddress*)addr1 withDestinationAddress:(BTLSocketAddress*)addr2
{
	if(addr1 == nil || addr2 == nil){
		return;
	}
	
	BTLUDPSocket* sock1 = (BTLUDPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_DGRAM];
	STAssertTrue([sock1 bindToAddress:addr1], nil);
	
	BTLUDPSocket* sock2 = (BTLUDPSocket*) [BTLSocket socketWithAddressFamily:[addr2 family] type:SOCK_DGRAM];
	STAssertTrue([sock2 bindToAddress:addr2], nil);
	
	BTLSocketBuffer* data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock1 writeData:data1 toAddress:[sock2 localAddress]];
	
	STAssertTrue([sock1 isConnected], nil);
	
	BTLSocketBuffer* testData = [sock2 read];
	
	while(testData == nil){
		testData = [sock2 read];
	}
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"First data not equal", nil);
	}
	
	STAssertTrue([sock2 isConnected], nil);
	
	data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock2 writeData:data1 toAddress:[sock1 localAddress]];
	
	testData = [sock2 read];
	
	while(testData == nil){
		testData = [sock1 read];
	}
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"Second data not equal");
	}
	
	[sock1 closeConnectionToAddress:[sock2 localAddress]];
	STAssertFalse([sock1 isConnected], nil);
	[sock2 connectionInterruptedToAddress:[sock1 localAddress]];
	STAssertFalse([sock2 isConnected], nil);
}

@end
